package com.example.zyl.common.query.aspect;

import cn.dev33.satoken.SaManager;
import cn.hutool.crypto.SecureUtil;
import com.example.zyl.common.CommonResult;
import com.example.zyl.common.enums.CommonCode;
import com.example.zyl.common.exception.ServiceException;
import com.example.zyl.common.query.annotation.ApiSubmit;
import com.example.zyl.common.utils.ServletUtils;
import com.example.zyl.common.utils.ToolUtils;
import com.example.zyl.common.utils.constant.RedisConstant;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;

import javax.servlet.http.HttpServletRequest;
import java.util.concurrent.TimeUnit;

@Slf4j
@Aspect
@Component
@RequiredArgsConstructor
public class RespSubmitAspect {

    private final RedisTemplate<String, String> redisTemplate;

    private static final ThreadLocal<String> KEY_CACHE = new ThreadLocal<>();

    @Before(value = "@annotation(com.example.zyl.common.query.annotation.ApiSubmit)")
    public void doBefore(JoinPoint point) throws Throwable {
        // 获取方法签名
        MethodSignature methodSignature = (MethodSignature) point.getSignature();
        // 获取注解
        ApiSubmit apiSubmit = methodSignature.getMethod().getAnnotation(ApiSubmit.class);
        // 获取间隔时间
        int interval = apiSubmit.interval();
        // 获取消息
        String message = apiSubmit.message();
        // 获取时间单位
        TimeUnit timeUnit = apiSubmit.timeUnit();

        // 间隔时间不能小于1秒
        if (interval < 1000) {
            throw new ServiceException("重复提交间隔时间不能小于'1'秒");
        }
        // 获取请求
        HttpServletRequest request = ServletUtils.getRequest();
        // 获取请求参数
        String params = ToolUtils.argsArrayToString(point.getArgs());

        // 请求地址（作为存放cache的key值）
        String url = "";
        // 唯一值（没有消息头则使用请求地址）
        String submitKey = "";
        if (request != null) {
            url = request.getRequestURI();
            submitKey = StringUtils.trimToEmpty(request.getHeader(SaManager.getConfig()
                    .getTokenName()));
        }
        // 加密请求参数
        submitKey = SecureUtil.md5(submitKey + ":" + params);

        //拼接key
        String cacheRepeatKey = RedisConstant.REPEAT_SUBMIT_KEY + url + submitKey;

        //判断是否重复提交
        Boolean isRepeat = redisTemplate.opsForValue().setIfAbsent(cacheRepeatKey, cacheRepeatKey, interval, timeUnit);

        //判断是否重复提交
        if (Boolean.FALSE.equals(isRepeat)) {
            log.info("重复提交，请求地址 ===> [{}]，参数 ===> [{}]", url, params);
            throw new ServiceException(message);
        }
        KEY_CACHE.set(cacheRepeatKey);
    }


    /**
     * 处理完请求后执行
     *
     * @param point 切点
     */
    @AfterReturning(pointcut = "@annotation(com.example.zyl.common.query.annotation.ApiSubmit)", returning = "jsonResult")
    public void doAfterReturning(JoinPoint point, Object jsonResult) {
        if (jsonResult instanceof CommonResult<?>) {
            try {
                CommonResult<?> result = (CommonResult<?>) jsonResult;
                // 成功则不删除redis数据 保证在有效时间内无法重复提交
                if (result.getCode() == CommonCode.SUCCESS.getCode()) {
                    return;
                }
                redisTemplate.delete(KEY_CACHE.get());
            } finally {
                KEY_CACHE.remove();
            }
        }
    }


    /**
     * 拦截异常操作
     *
     * @param joinPoint 切点
     * @param e         异常
     */
    @AfterThrowing(value = "@annotation(com.example.zyl.common.query.annotation.ApiSubmit)", throwing = "e")
    public void doAfterThrowing(JoinPoint joinPoint, Exception e) {
        redisTemplate.delete(KEY_CACHE.get());
        KEY_CACHE.remove();
    }
}
